using System;
using System.Collections.Generic;
using System.Text;
using Emlyn.Common.Data.SqlServer.SqlManagers;
using System.Data.SqlClient;
using System.Reflection;
//using Emlyn.Common.Data.Configuration;
using Emlyn.Website.Users;

namespace Emlyn.Common.Data.DBUpdater
{
    public class DBUpdaterMain
    {
        private static string _websiteCode;

        public static string WebsiteCode
        {
            get { return _websiteCode; }
            set { _websiteCode = value; }
        }

        public int GetDBVersion()
        {
            int retval = 0; // default version

            if (SqlManager.TableExists("tblEOWebsite"))
            {
                EWebsiteQuery lwebsiteQuery = new EWebsiteQuery();
                EWebsite lwebsite = lwebsiteQuery.Select(WebsiteCode);
                retval = lwebsite.Version;
            }
            // else use default version 0

            return retval;
        }

        public bool NeedsUpgrade(int aTargetVersion)
        {
            return aTargetVersion > GetDBVersion();
        }

        public bool NeedsUpgrade()
        {
            return NeedsUpgrade(LatestVersion());
        }

        public Assembly GetCurrentAssembly()
        {
            return Assembly.GetExecutingAssembly();
        }

        public List<Type> GetDBUpdaterRuleTypes()
        {
            List<Type> retval = new List<Type>();

            foreach (Assembly lAssembly in System.AppDomain.CurrentDomain.GetAssemblies())
            {
                foreach (Type ltype in lAssembly.GetTypes())
                {
                    if (ltype.GetInterface(typeof(IDBUpdaterRule).FullName) != null)
                    {
                        retval.Add(ltype);
                    }
                }
            }

            return retval;
        }

        public int LatestVersion()
        {
            int retval = 0; // default

            foreach (Type ltype in GetDBUpdaterRuleTypes())
            {
                retval = Math.Max(
                                retval,
                                (ltype.Assembly.CreateInstance(ltype.FullName) as IDBUpdaterRule).UpgradeToVersion()
                );
            }

            return retval;
        }

        public void UpgradeDB()
        {
            UpgradeDB(LatestVersion());
        }

        public void UpgradeDB(int aTargetVersion)
        {
            List<IDBUpdaterRule> lUpdatersInOrder = GetDBUpdaterList(aTargetVersion);
            int lCurrentVersion = GetDBVersion();

            if (lUpdatersInOrder.Count == 0 ||
                lUpdatersInOrder[lUpdatersInOrder.Count - 1].UpgradeToVersion() < aTargetVersion)
            {
                throw new NoUpdatePathException();
            }

            foreach (IDBUpdaterRule lrule in lUpdatersInOrder)
            {
                if (lrule.UpgradeFromVersion() >= lCurrentVersion)
                {
                    // no transaction handling here, should have some
                    lrule.PerformUpgrade();

                    UpdateVersion(lrule.UpgradeToVersion());
                }
            }
        }

        public void UpdateVersion(int aVersion)
        {
            EWebsiteQuery lwebsiteQuery = new EWebsiteQuery();
            EWebsite lwebsite = lwebsiteQuery.Select(WebsiteCode);
            lwebsite.Version = aVersion;
            EWebsiteMod lwebsiteMod = new EWebsiteMod();
            lwebsiteMod.Save(lwebsite);
        }

        public List<IDBUpdaterRule> GetDBUpdaterList(int aMaxVersion)
        {
            List<IDBUpdaterRule> lAllDBUpdaters = new List<IDBUpdaterRule>();

            foreach (Type ltype in GetDBUpdaterRuleTypes())
            {
                lAllDBUpdaters.Add(ltype.Assembly.CreateInstance(ltype.FullName) as IDBUpdaterRule);
            }

            // now keep only rules which move the version forward

            List <IDBUpdaterRule> lForwardDBUpdaters = new List<IDBUpdaterRule>();

            foreach (IDBUpdaterRule lrule in lAllDBUpdaters)
            {
                if (
                    (lrule.UpgradeFromVersion() >= 0) && 
                    (lrule.UpgradeToVersion() > lrule.UpgradeFromVersion()) &&
                    (lrule.UpgradeToVersion() <= aMaxVersion)
                    )
                    lForwardDBUpdaters.Add(lrule);
            }

            // now construct an ordered list to update in a straight line to aMaxVersion
            int llast = 0;
            int lprevlast = -1;
            List<IDBUpdaterRule> lOrderedUpdaters = new List<IDBUpdaterRule>();

            while (llast > lprevlast)
            {
                lprevlast = llast;

                foreach (IDBUpdaterRule lrule in lForwardDBUpdaters)
                {
                    if (lrule.UpgradeFromVersion() == llast)
                    {
                        lOrderedUpdaters.Add(lrule);
                        llast = lrule.UpgradeToVersion();
                        break;
                    }
                }
            }

            return lOrderedUpdaters;
        }
    }
}
